<template>
  <!-- 聊天框容器 -->
  <div class="chat-box">
    <!-- TDesign 的聊天组件 -->
    <t-chat
      ref="chatRef"
      clear-history
      :data="chatList"
      :text-loading="loading"
      :is-stream-load="isStreamLoad"
      style="height: 100%"
      @scroll="handleChatScroll"
      @clear="clearConfirm"
    >
      <!-- 自定义聊天消息内容模板 -->
      <template #content="{ item, index }">
        <!-- 如果消息包含推理内容，显示推理组件 -->
        <t-chat-reasoning
          v-if="item.reasoning?.length > 0"
          expand-icon-placement="right"
        >
          <!-- 推理组件头部模板 -->
          <template #header>
            <!-- 流式数据加载中，显示加载提示 -->
            <t-chat-loading v-if="isStreamLoad" text="思考中..." />
            <!-- 加载完成，显示已深度思考状态 -->
            <div v-else style="display: flex; align-items: center">
              <CheckCircleIcon
                style="
                  color: var(--td-success-color-5);
                  font-size: 20px;
                  margin-right: 8px;
                "
              />
              <span>已深度思考</span>
            </div>
          </template>
          <!-- 显示推理内容 -->
          <t-chat-content
            v-if="item.reasoning.length > 0"
            :content="item.reasoning"
          />
        </t-chat-reasoning>
        <!-- 显示普通消息内容 -->
        <t-chat-content
          v-if="item.content.length > 0"
          :content="item.content"
        />
      </template>
      <!-- 自定义聊天消息操作模板 -->
      <template #actions="{ item, index }">
        <t-chat-action
          :content="item.content"
          :operation-btn="['good', 'bad', 'replay', 'copy']"
          @operation="handleOperation"
        />
      </template>
      <!-- 自定义聊天输入框模板 -->
      <template #footer>
        <t-chat-input
          :stop-disabled="isStreamLoad"
          @send="inputEnter"
          @stop="onStop"
        >
        </t-chat-input>
      </template>
    </t-chat>
    <!-- 显示回到底部按钮 -->
    <t-button
      v-show="isShowToBottom"
      variant="text"
      class="bottomBtn"
      @click="backBottom"
    >
      <div class="to-bottom">
        <ArrowDownIcon />
      </div>
    </t-button>
  </div>
</template>

<script setup>
import { ref } from "vue";
// 引入模拟 SSE 请求的响应类
import { MockSSEResponse } from "./mock-data/sseRequest-reasoning";
// 引入图标组件
import { ArrowDownIcon, CheckCircleIcon } from "tdesign-icons-vue-next";

// 存储取消请求的对象
const fetchCancel = ref(null);
// 加载状态
const loading = ref(false);
// 流式数据加载状态
const isStreamLoad = ref(false);

// 聊天组件的引用
const chatRef = ref(null);
// 是否显示回到底部按钮
const isShowToBottom = ref(false);

// 滚动到底部的方法
const backBottom = () => {
  chatRef.value.scrollToBottom({
    behavior: "smooth",
  });
};

// 处理聊天组件滚动事件，判断是否显示回到底部按钮
const handleChatScroll = function ({ e }) {
  const scrollTop = e.target.scrollTop;
  isShowToBottom.value = scrollTop < 0;
};

// 清空聊天消息列表
const clearConfirm = function () {
  chatList.value = [];
};

// 处理聊天消息操作事件
const handleOperation = function (type, options) {
  console.log("handleOperation", type, options);
};

// 聊天消息列表，倒序渲染
const chatList = ref([
  {
    content: `模型由<span>hunyuan</span>变为<span>GPT4</span>`,
    role: "model-change",
    reasoning: "",
  },
  {
    avatar: "https://tdesign.gtimg.com/site/chat-avatar.png",
    name: "TDesignAI",
    datetime: "今天16:38",
    reasoning: "",
    content:
      "它叫 McMurdo Station ATM，是美国富国银行安装在南极洲最大科学中心麦克默多站的一台自动提款机。",
    role: "assistant",
    duration: 10,
  },
  {
    avatar: "https://tdesign.gtimg.com/site/avatar.jpg",
    name: "自己",
    datetime: "今天16:38",
    content: "南极的自动提款机叫什么名字？",
    role: "user",
    reasoning: "",
  },
]);

// 停止流式数据加载
const onStop = function () {
  if (fetchCancel.value) {
    fetchCancel.value.controller.close();
    loading.value = false;
    isStreamLoad.value = false;
  }
};

// 处理用户输入并发送消息
const inputEnter = function (inputValue) {
  // 如果正在流式加载，不处理输入
  if (isStreamLoad.value) {
    return;
  }
  // 如果输入为空，不处理
  if (!inputValue) return;
  // 构建用户消息对象
  const params = {
    avatar: "https://tdesign.gtimg.com/site/avatar.jpg",
    name: "自己",
    datetime: new Date().toDateString(),
    content: inputValue,
    role: "user",
  };
  // 将用户消息添加到消息列表头部
  chatList.value.unshift(params);
  // 构建占位消息对象，用于显示 AI 回复
  const params2 = {
    avatar: "https://tdesign.gtimg.com/site/chat-avatar.png",
    name: "TDesignAI",
    datetime: new Date().toDateString(),
    content: "",
    reasoning: "",
    role: "assistant",
  };
  // 将占位消息添加到消息列表头部
  chatList.value.unshift(params2);
  // 处理数据请求
  handleData(inputValue);
};

// 处理 SSE 请求
const fetchSSE = async (fetchFn, options) => {
  // 发起请求
  const response = await fetchFn();
  const { success, fail, complete } = options;
  // 如果请求失败，调用 complete 和 fail 回调
  if (!response.ok) {
    complete?.(false, response.statusText);
    fail?.();
    return;
  }
  // 获取响应体的读取器
  const reader = response?.body?.getReader();
  const decoder = new TextDecoder();
  // 如果没有读取器，直接返回
  if (!reader) return;

  // 读取响应数据
  reader.read().then(function processText({ done, value }) {
    if (done) {
      // 数据读取完成，调用 complete 回调
      complete?.(true);
      return;
    }
    // 解码数据块
    const chunk = decoder.decode(value, { stream: true });
    // 分割数据块
    const buffers = chunk.toString().split(/\r?\n/);
    // 解析 JSON 数据
    const jsonData = JSON.parse(buffers);
    // 调用成功回调
    success(jsonData);
    // 继续读取数据
    reader.read().then(processText);
  });
};

// 处理数据请求
const handleData = async () => {
  // 设置加载状态
  loading.value = true;
  // 设置流式加载状态
  isStreamLoad.value = true;
  // 获取最新的消息对象
  const lastItem = chatList.value[0];
  // 模拟响应数据
  const mockedData = {
    reasoning: `嗯，用户问牛顿第一定律是不是适用于所有参考系。首先，我得先回忆一下牛顿第一定律的内容。牛顿第一定律，也就是惯性定律，说物体在没有外力作用时会保持静止或匀速直线运动。也就是说，保持原来的运动状态。

那问题来了，这个定律是否适用于所有参考系呢？记得以前学过的参考系分惯性系和非惯性系。惯性系里，牛顿定律成立；非惯性系里，可能需要引入惯性力之类的修正。所以牛顿第一定律应该只在惯性参考系中成立，而在非惯性系中不适用，比如加速的电梯或者旋转的参考系，这时候物体会有看似无外力下的加速度，所以必须引入假想的力来解释。`,
    content: `牛顿第一定律（惯性定律）**并不适用于所有参考系**，它只在**惯性参考系**中成立。以下是关键点：

---

### **1. 牛顿第一定律的核心**
- **内容**：物体在不受外力（或合力为零）时，将保持静止或匀速直线运动状态。
- **本质**：定义了惯性系的存在——即存在一类参考系，在其中惯性定律成立。`,
  };
  // 创建模拟 SSE 响应对象
  const mockResponse = new MockSSEResponse(mockedData);
  // 存储取消请求的对象
  fetchCancel.value = mockResponse;
  // 发起 SSE 请求
  await fetchSSE(
    () => {
      return mockResponse.getResponse();
    },
    {
      success(result) {
        console.log("success", result);
        // 停止加载状态
        loading.value = false;
        // 更新推理内容
        lastItem.reasoning += result.delta.reasoning_content;
        // 更新消息内容
        lastItem.content += result.delta.content;
      },
      complete(isOk, msg) {
        if (!isOk) {
          // 请求失败，设置错误信息
          lastItem.role = "error";
          lastItem.content = msg;
          lastItem.reasoning = msg;
        }
        // 显示用时 xx 秒，业务侧需要自行处理
        lastItem.duration = 20;
        // 停止流式加载状态
        isStreamLoad.value = false;
        // 停止加载状态
        loading.value = false;
      },
    }
  );
};
</script>

<style lang="less" scoped>
/* 应用滚动条样式 */
::-webkit-scrollbar-thumb {
  background-color: var(--td-scrollbar-color);
}
::-webkit-scrollbar-thumb:horizontal:hover {
  background-color: var(--td-scrollbar-hover-color);
}
::-webkit-scrollbar-track {
  background-color: var(--td-scroll-track-color);
}

.chat-box {
  position: relative;
  height: 100%;
  padding: 20px 10px;
  box-sizing: border-box;

  .bottomBtn {
    position: absolute;
    left: 50%;
    margin-left: -20px;
    bottom: 210px;
    padding: 0;
    border: 0;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, 0.08),
      0px 16px 24px 2px rgba(0, 0, 0, 0.04),
      0px 6px 30px 5px rgba(0, 0, 0, 0.05);
  }

  .to-bottom {
    width: 40px;
    height: 40px;
    border: 1px solid #dcdcdc;
    box-sizing: border-box;
    background: var(--td-bg-color-container);
    border-radius: 50%;
    font-size: 24px;
    line-height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;

    .t-icon {
      font-size: 24px;
    }
  }
}

.model-select {
  display: flex;
  align-items: center;

  .t-select {
    width: 112px;
    height: 32px;
    margin-right: 8px;

    .t-input {
      border-radius: 32px;
      padding: 0 15px;
    }
  }

  .check-box {
    width: 112px;
    height: 32px;
    border-radius: 32px;
    border: 0;
    background: #e7e7e7;
    color: rgba(0, 0, 0, 0.9);
    box-sizing: border-box;
    flex: 0 0 auto;

    .t-button__text {
      display: flex;
      align-items: center;
      justify-content: center;

      span {
        margin-left: 4px;
      }
    }
  }

  .check-box.is-active {
    border: 1px solid #d9e1ff;
    background: #f2f3ff;
    color: var(--td-brand-color);
  }
}
</style>